<?php
/**     
*
* Copyright (c) 2009, Persistent Systems Limited
*
* Redistribution and use, with or without modification, are permitted 
*  provided that the following  conditions are met:
*   - Redistributions of source code must retain the above copyright notice, 
*     this list of conditions and the following disclaimer.
*   - Neither the name of Persistent Systems Limited nor the names of its contributors 
*     may be used to endorse or promote products derived from this software 
*     without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
 *  For getting properties and methods related to CURL 
 */ 
require_once 'util/curlRequest.php';

/**
 *  For ACS constants
 */ 
require_once 'ACS/ACSConstants.php';

/**
 *  For getting properties and methods related to SharedSecret Token 
 */ 
require_once 'ACS/SharedSecret.php';

/**
 *  For getting properties and methods related to SAML Token 
 */ 
require_once 'ACS/SAML.php';

/**
 *  For getting config options
 */ 
require_once 'ServiceBus/constants.inc';

/**
 *  For getting properties and methods related to HTTP 
 */ 
require_once 'util/HttpHelper.php';    

/**
 *  For getting Audit Log related properties and Methods
 */ 
require_once 'util/LoggerClass.php';     

/**
 * Class MessageBufferClient For create/send/get/delete Message Buffer
 * @access public
 */
class MessageBufferClient
{
    /**
     * Content type to be set for atom entry
     *
     * @var string $atomEntryContentType
     * @access private
     */     
    private static $atomEntryContentType = "application/atom+xml;type=entry;charset=utf-8";
    
    /**
     * credential object with required properties
     *
     * @var object $credential
     * @access private
     */  
    private $credential;
    
    /**
     * Default timeout for token in seconds
     *
     * @var string $defaultGetTokenTimeout
     * @access private
     */  
    private static  $defaultGetTokenTimeout;

    /**
     * Default timeout for reciving message in seconds
     *
     * @var string $atomEntryContentType
     * @access private
     */  
    private static  $defaultReceiveTimeout;

    /**
     * Default timeout for sending message in seconds
     *
     * @var string $defaultSendTimeout
     * @access private
     */  
    private static $defaultSendTimeout;

    /**
     * Message buffer policy (atom entry)
     *
     * @var string $messageBufferPolicy
     * @access private
     */  
    private $messageBufferPolicy;

    /**
     * The URI to point the message buffer
     *
     * @var string $messageBufferUri
     * @access private
     */  
    private $messageBufferUri;

    /**
     * The URI to point the message buffer
     *
     * @var string $httpWebProxy
     * @access public
     */ 
    public $httpWebProxy;
    
    /**
     * Relative path (message buffer URI) to recieve messages from 
     * message buffer
     *
     * @var string $receivePath
     * @access private
     */  
    const receivePath = "messages/head";

    /**
     * Relative path (message buffer URI) to send messages to 
     * message buffer
     *
     * @var string $sendPath
     * @access private
     */  
    const  sendPath = "messages";

    /**
     * timeout paramater to be sent with the requests to messsage
     * buffer such as send or recieve message
     *
     * @var string $timeoutParameter
     * @access private
     */  
    const  timeoutParameter = "timeout";
    
    /**
     * object of curl class
     *
     * @var object $_curlRequest
     * @access private
     */  
    private $_curlRequest;

    /**
     * class constructor
     *
     * @param string $messageBufferUri message buffer URI
     * @param object $credential       object of Credential class @see Credential
     * @param object $messageBufferPolicy object of class MessageBufferPolicy 
     * see @messageBufferPolicy
     */
    function __construct($messageBufferUri,$scope,$messageBufferPolicy,$httpWebProxy = null)
    {
        if (!isset($messageBufferUri))
        {
            throw new Exception("messageBufferUri not present");
        }
        
        //parse messageBufferURI
        $messageBufferComponents = parse_url($messageBufferUri);

        if (isset($messageBufferComponents['query']))
        {
            throw new Exception("messageBufferUri URI cannot have any query parameters");
        }
        
        if (!isset($messageBufferPolicy))
        {
            throw new Exception("messageBufferPolicy not present");
        }
        
        MessageBufferClient::$defaultGetTokenTimeout = 60;

        $this->messageBufferUri = $messageBufferUri;
        $this->scope = $scope;
        $this->messageBufferPolicy = $messageBufferPolicy;
        $this->httpWebProxy = $httpWebProxy;
    }
    
    /**
     * method to create Message Buffer
     *
     * @param object $messageBufferPolicy object of class MessageBufferPolicy 
     * see @messageBufferPolicy
     */ 
    function createMessageBuffer($messageBufferPolicy)
    {        
        if (!isset($messageBufferPolicy))
        {
            throw new Exception("messageBufferPolicy not present");
        }
    
        //get policy in atom string format
        $policy = $messageBufferPolicy->getPolicyXml();        

        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();

        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);

        //url to which curl transfer is to be done. This must be secured with v1
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->messageBufferUri);        
                
        // http method for creating message buffer is PUT
        $this->_curlRequest->httpVerb = PUT_METHOD;

        // http method for creating message buffer is PUT
        $this->_curlRequest->varifyTransfer = false;
        
        //set the messsage buffer policy
        $this->_curlRequest->putFields = $policy;
         
        //the the applies to URL to be message buffer URL0
        $appliesTo = $this->messageBufferUri;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);
            
        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
        
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';
        
        $header['Content-Type'] = "application/atom+xml;type=entry;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;
        
        //process the curl transfer
        $messageBufferResponse = $this->_curlRequest->process();

        //Do Audit logging if set to true
        if (LoggerClass::$doAuditLog) 
        {
            $requestInfo = $this->_curlRequest->getInfo();
            LoggerClass::writeLogEntry(REQUEST_TO_SERVICEBUS.'-'.CREATE_MSG_BUFFER, $requestInfo);
            LoggerClass::writeLogEntry(RESPONSE_FROM_SERVICEBUS.'-'.CREATE_MSG_BUFFER, $messageBufferResponse);
        } 
        
        $this->_curlRequest->close();
        unset($this->_curlRequest);
        return $messageBufferResponse;
    }

    /**
     * method to get Message  buffer Policy
     *
     * @param object $messageBufferPolicy object of class MessageBufferPolicy 
     * see @messageBufferPolicy
     */
    function getPolicy($messageBufferPolicy )
    {         
        //get policy in atom string format
        $policy = $messageBufferPolicy->getPolicyXml();
        
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();

        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);

        //url to which curl transfer is to be done. This must be secured with v1
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->messageBufferUri);

        // http method for creating message buffer is PUT
        $this->_curlRequest->httpVerb= GET_METHOD;

        //the the applies to URL to be message buffer URL0
        $appliesTo = $this->messageBufferUri;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);
        
        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';

        $header['Content-Type'] = "application/atom+xml;type=entry;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //process the curl transfer    and returns the  Message buffer name
        $response = $this->_curlRequest->process();
        $this->_curlRequest->close();
        unset($this->_curlRequest);
        return $response;
    }

    /**
     * method to Delete Message Buffer
     *
     * @param object $messageBufferPolicy object of class MessageBufferPolicy 
     * see @messageBufferPolicy
     */
    function DeleteMessageBuffer($messageBufferPolicy )
    {         
        //get policy in atom string format
        $policy = $messageBufferPolicy->getPolicyXml();

        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();

        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);

        //url to which curl transfer is to be done. This must be secured with v1
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->messageBufferUri);

         // http method for creating message buffer is PUT
        $this->_curlRequest->httpVerb= DELETE_METHOD;
         
        //the the applies to URL to be message buffer URL
        $appliesTo = $this->messageBufferUri;

        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);
        
        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';

        $header['Content-Type'] = "application/atom+xml;type=entry;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //process the curl transfer
        $this->_curlRequest->process();
      
        $this->_curlRequest->close();
        unset($this->_curlRequest);
    }
    
    /**
     * method to Send Message
     *
     * @param string  $message Message Body
     * @return ATOM response
     */
    function sendMessage($message, $timeout = '')
    {         
        $message = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1"/></s:Header><s:Body><string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">'.htmlentities($message).'</string></s:Body></s:Envelope>';

        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();

        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);

        if ($timeout <= 0)
        {
            $timeout = MessageBufferClient::$defaultGetTokenTimeout;
        }
        
        //This must be secured with v1
        $this->_curlRequest->url = $this->messageBufferUri . MessageBufferClient::sendPath. "?" . MessageBufferClient::timeoutParameter . "=" . $timeout;
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->_curlRequest->url);        
    
        //set the messsage buffer policy
        $this->_curlRequest->postFields= $message;

        //set the messsage buffer policy
        $this->_curlRequest->httpVerb= POST_METHOD;
         
        //the the applies to URL  
        $appliesTo = $this->messageBufferUri.MessageBufferClient::sendPath;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);
        
        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';

        $header['Content-Type'] = "text/xml;charset=utf-8";
        $header['Authorization'] = $headerValue; 
         
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //process the curl transfer
        $msgBufferSendResponseInfo = $this->_curlRequest->process();
     
        //Do Audit logging if set to true
        if (LoggerClass::$doAuditLog)
        {
            $msgBufferSendRequestResponseInfo = $this->_curlRequest->getInfo();
            LoggerClass::writeLogEntry(REQUEST_TO_SERVICEBUS.'-'.SEND_MSG, $msgBufferSendRequestResponseInfo);
            LoggerClass::writeLogEntry(RESPONSE_FROM_SERVICEBUS.'-'.SEND_MSG, $msgBufferSendResponseInfo);
        } 
        
        $this->_curlRequest->close();
        unset($this->_curlRequest);
    
        return $msgBufferSendResponseInfo;     
    }
    
    /**
     * method to Get Message From Message Buffer
     * @param int $timeout Message timeout from message buffer
     * @return ATOM response
     */
    function getMessage($timeout = '')
    {
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();
        
        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);
        
        if ($timeout <= 0)
        {
            $timeout = MessageBufferClient::$defaultGetTokenTimeout;
        } 

        $this->_curlRequest->url = $this->messageBufferUri.MessageBufferClient::receivePath."?".MessageBufferClient::timeoutParameter."=".$timeout;
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->_curlRequest->url);
    
        //set the messsage buffer policy
        $this->_curlRequest->httpVerb= DELETE_METHOD;
         
        //the the applies to URL  
        $appliesTo = $this->messageBufferUri.MessageBufferClient::receivePath;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);

        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';        
     
        $header['Content-Type'] = "text/xml;charset=utf-8";
        $header['Authorization'] = $headerValue; 
     
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;
        
        //process the curl transfer
        $getMessageResponse = $this->_curlRequest->process();
        
        //Do Audit logging if set to true
        if (LoggerClass::$doAuditLog)
        {
            $getMessageRequestResposeInfo = $this->_curlRequest->getInfo();
            LoggerClass::writeLogEntry(REQUEST_TO_SERVICEBUS.'-'.GET_MSG, $getMessageRequestResposeInfo);
            LoggerClass::writeLogEntry(RESPONSE_FROM_SERVICEBUS.'-'.GET_MSG, $getMessageResponse);
        } 
        $this->_curlRequest->close();
        unset($this->_curlRequest);

        //process the curl transfer
        return $getMessageResponse;         
    }
    
    // Extract raw message body from response
    function extractMessageBodyFromXml($message)
    {
        if (strpos($message,'<s:Envelope') === 0)
        {
            $search = '<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">';
            $start = strpos($message, $search);
            $end = strpos($message, "</string>");

            return substr($message, $start + strlen($search), $end - $start -  strlen($search));
        }
    }
    
    /**
     * methods to gets the first unlocked message and locks it.
     * It returns array contaning message content, message URI, lock duration and lock URI         
     */
    function peekLock()
    {
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();

        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);
                                
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest->url = $this->messageBufferUri.MessageBufferClient::receivePath."?".MessageBufferClient::timeoutParameter."=".MessageBufferClient::$defaultGetTokenTimeout."&lockduration=120";            
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->_curlRequest->url);
    
        //set the messsage buffer policy
        $this->_curlRequest->httpVerb = POST_METHOD;
         
        //the the applies to URL  
        $appliesTo = $this->messageBufferUri.MessageBufferClient::receivePath;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);        

        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';
    
        $header['Content-Type'] = "text/xml;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        $header['Accept'] = "*/*";
         
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //set the auth headers to curl transfer 
        $this->_curlRequest->Header = true;

        //process the curl transfer
        $response  = $this->_curlRequest->process();
        
        $this->_curlRequest->close();
        unset($this->_curlRequest);
        
        return (HttpHelper::http_parse_headers($response));          
    }

    /**
     * method to delete locked message from the message buffer by making a DELETE 
     * @param messageUri Value of message uri
     * @param lockId Value of lockId
     * @return 
     */
    function deleteLockedMessage($messageUri, $LockId)
    {
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();
        
        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);
        
        $this->_curlRequest->url = $messageUri."?lockid=".$LockId;
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->_curlRequest->url);
    
        //set the messsage buffer policy
        $this->_curlRequest->httpVerb = DELETE_METHOD;
         
        //the the applies to URL        
        $appliesTo = $messageUri;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);

        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';
    
        $header['Content-Type'] = "text/xml;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        //$header['Expect'] = "100-continue"; 
        $header['Accept'] = "*/*"; 
         
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //set the auth headers to curl transfer 
        $this->_curlRequest->Header = true;

        //process the curl transfer
        $this->_curlRequest->process();

        $this->_curlRequest->close();
        unset($this->_curlRequest);
    }

    /**
     * method to release locked message from the message buffer by making a DELETE 
     * @param lockUri Value of lockUri        
     * @return 
     */
    function releaseLock($lockUri)
    {
        //create an object to facilate curl transfer @see CurlRequest
        $this->_curlRequest = new CurlRequest();
        
        //set the proxy credentials for curl request
        $this->_curlRequest->setHttpWebProxy($this->httpWebProxy);

        $this->_curlRequest->url = $lockUri;
        $this->_curlRequest->url = str_replace('http://', 'https://', $this->_curlRequest->url);
    
        //set the messsage buffer policy
        $this->_curlRequest->httpVerb = DELETE_METHOD;
         
        //the the applies to URL  
        $appliesTo = $lockUri;
        
        //get the desired token based on Authentication Type
        $messageBufferToken = $this->getToken($appliesTo);        

        $wrap_access_token = '';
        parse_str($messageBufferToken);
        $token = $wrap_access_token;
          
        //set the token to service bus request header    
        $headerValue = WRAP_AUTHENTICATION_TYPE . ' ' . WRAP_AUTHORIZATION_HEADER_KEY . '="' .  $token . '"';
    
        $header['Content-Type'] = "text/xml;charset=utf-8";
        $header['Authorization'] = $headerValue; 
        //$header['Expect'] = "100-continue"; 
        $header['Accept'] = "*/*"; 
                 
        //set the auth headers to curl transfer 
        $this->_curlRequest->httpHeaders = $header;

        //process the curl transfer
        $this->_curlRequest->process();

        $this->_curlRequest->close();
        unset($this->_curlRequest);
    }

    /**
     * method to release locked message from the message buffer by making a DELETE 
     * @param string $appliesTo    Applies To String
     * @return String Token
     */
    function getToken($appliesTo)
    {
        //code to add ACS token to service bus request    
        //select token provider based on Authentication type
        
        switch (Scope::$credentialType)
        {
            case SHARED_SECRET:                
                $tokenProvider = new SharedSecret(
                DotNetServicesEnvironment::getACMHostName(),
                $this->scope->getServiceNamespaceDomain());
                $serviceUrl = $this->getAcsServiceBusUri();
                $this->scope->setServiceURI($serviceUrl);
                break;
                
            case SAML:
                $tokenProvider = new SAML(
                DotNetServicesEnvironment::getACMHostName(),
                $this->scope->getServiceNamespaceDomain()); // TBD
                break;

            case SIMPLE_API_AUTH : 
                $tokenProvider = new SimpleApiAuthService(
                DotNetServicesEnvironment::getACMHostName(),
                $this->scope->getServiceNamespaceDomain()); //TBD
                break;
        }

        $tokenProvider->setHttpWebProxy($this->httpWebProxy);  
        $this->scope->setAppliesTo($appliesTo);
        $tokenProvider->setScope($this->scope );
        
        // get the required token
        $token = $tokenProvider->getACSToken();
        return $token;
    }

    /**
     * method to release locked message from the message buffer by making a DELETE 
     * @return string Service bus URI
     */
    function getAcsServiceBusUri()
    {
        return 'https://' . $this->scope->getServiceNamespaceDomain()
            . '-sb.' . DotNetServicesEnvironment::getACMHostName()
            . '/'. WRAP_PROTOCOL_VERSION;
    }    
}
?>